package com.lncg.service.impl;

import com.lncg.entity.DeviceSendData;
import com.lncg.entity.DeviceReceiveData;
import com.lncg.mapper.DeviceSendDataMapper;
import com.lncg.service.DeviceSendDataService;
import com.lncg.service.ITcpConnectionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;

/**
 * 设备下发数据服务实现类
 * 
 * 支持四种协议格式：
 * 1. 32字节确认指令（服务端下发确认消息）
 * 2. 70字节下发数据指令（服务端下发数据消息）
 * 3. 32字节上行确认指令（表端回复确认消息）
 * 4. 92字节上行数据指令（表端上报数据）
 */
@Service
public class DeviceSendDataServiceImpl implements DeviceSendDataService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceSendDataServiceImpl.class);

    @Autowired
    private DeviceSendDataMapper deviceSendDataMapper;

    @Autowired
    private ITcpConnectionService tcpConnectionService;

    /**
     * 处理设备数据接收后的下发逻辑
     * @param deviceData 接收到的设备数据
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     * @return
     */
    @Override
    public boolean processAfterReceive(DeviceReceiveData deviceData, String clientIp, int clientPort) {
        try {
            String imei = deviceData.getImei();
            DeviceSendData pendingData = findPendingData(imei);
            
            if (pendingData != null) {
                logger.info("下发数据 IMEI:{} ID:{}", imei, pendingData.getId());
                // 调试输出移除
                byte[] dataCommand = generateDataCommand(pendingData.getInstructionId(), imei, pendingData);
                boolean sendSuccess = sendDataCommand(clientIp, clientPort, dataCommand);
                
                if (!sendSuccess) {
                    logger.warn("下发数据发送失败 IMEI:{}", imei);
                }
                return sendSuccess;
            } else {
                // 无待下发数据：仍然发送确认指令，但返回false，避免上层误判为“有下发数据”
                byte[] confirmCommand = generateConfirmCommand(deviceData.getInstructionId(), imei);
                try {
                    sendConfirmCommand(clientIp, clientPort, confirmCommand);
                } catch (Exception ignore) {
                }
                return false;
            }
        } catch (Exception e) {
            logger.error("下发处理失败 IMEI:{} 错误:{}", deviceData.getImei(), e.getMessage());
            return false;
        }
    }

    /**
     * 根据IMEI查询待下发的数据
     * @param imei 设备IMEI
     * @return
     */
    @Override
    public DeviceSendData findPendingData(String imei) {
        try {
            return deviceSendDataMapper.findLatestPendingByImei(imei);
        } catch (Exception e) {
            logger.error("查询待下发数据失败 IMEI:{} 错误:{}", imei, e.getMessage());
            return null;
        }
    }

    /**
     * 生成32字节确认指令
     * @param instructionId 指令编号
     * @param imei 设备IMEI
     * @return
     */
    @Override
    public byte[] generateConfirmCommand(String instructionId, String imei) {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(32);
            
            // 1. 前导符：[1-2] 共2字节，0x43 0x47
            buffer.put((byte) 0x43);
            buffer.put((byte) 0x47);
            
            // 2. 指令编号：[3-12] 共10字节（数据库保存为十六进制字符串，需还原为原始10字节）
            byte[] instructionBytes = decodeInstructionId(instructionId);
            byte[] paddedInstruction = new byte[10];
            System.arraycopy(instructionBytes, 0, paddedInstruction, 0, Math.min(instructionBytes.length, 10));
            buffer.put(paddedInstruction);
            
            // 3. 指令类型：[13-13] 共1字节，0x02：服务端下发确认指令
            buffer.put((byte) 0x02);
            
            // 4. IMEI：[14-29] 共16字节，将收到的IMEI封装返回
            byte[] imeiBytes = imei.getBytes(StandardCharsets.US_ASCII);
            byte[] paddedImei = new byte[16];
            System.arraycopy(imeiBytes, 0, paddedImei, 0, Math.min(imeiBytes.length, 16));
            buffer.put(paddedImei);
            
            // 5. CRC16：[30-31] 共2字节，CRC16 ModBus校验
            // CRC16从第3位开始至第29位，共27字节（跳过前导符）
            byte[] dataForCrc = new byte[27];
            buffer.position(2); // 跳过前导符
            buffer.get(dataForCrc);
            
            short crc16 = calculateCrc16(dataForCrc);
            buffer.put((byte) (crc16 & 0xFF));        // 低位在前
            buffer.put((byte) ((crc16 >> 8) & 0xFF)); // 高位在后
            
            // 6. 结束符：[32-32] 共1字节，0x16
            buffer.put((byte) 0x16);
            
            return buffer.array();
        } catch (Exception e) {
            logger.error("生成确认指令失败: {}", e.getMessage());
            throw new RuntimeException("生成确认指令失败", e);
        }
    }

    /**
     * 生成70字节下发数据指令
     * @param instructionId 指令编号
     * @param imei 设备IMEI
     * @param sendData 下发数据
     * @return
     */
    @Override
    public byte[] generateDataCommand(String instructionId, String imei, DeviceSendData sendData) {
        try {
            ByteBuffer buffer = ByteBuffer.allocate(70);
            
            // 1. 前导符：[1-2] 共2字节，0x43 0x47
            buffer.put((byte) 0x43);
            buffer.put((byte) 0x47);
            
            // 2. 指令编号：[3-12] 共10字节（数据库保存为十六进制字符串，需还原为原始10字节）
            byte[] instructionBytes = decodeInstructionId(instructionId);
            byte[] paddedInstruction = new byte[10];
            System.arraycopy(instructionBytes, 0, paddedInstruction, 0, Math.min(instructionBytes.length, 10));
            buffer.put(paddedInstruction);
            
            // 3. 指令类型：[13-13] 共1字节，0x03：服务端下发数据指令
            buffer.put((byte) 0x03);
            
            // 4. IMEI：[14-29] 共16字节，将收到的IMEI封装返回
            byte[] imeiBytes = imei.getBytes(StandardCharsets.US_ASCII);
            byte[] paddedImei = new byte[16];
            System.arraycopy(imeiBytes, 0, paddedImei, 0, Math.min(imeiBytes.length, 16));
            buffer.put(paddedImei);

            // 5. 表号：[30-45] 共16字节，将表号转换成HEX
            String meterCode = sendData.getMeterCode();
            if (meterCode != null && meterCode.equalsIgnoreCase("NONE")) {
                // 如果表号是NONE，将字节全置FF
                for (int i = 0; i < 16; i++) {
                    buffer.put((byte) 0xFF);
                }
            } else if (meterCode != null && !meterCode.isEmpty()) {
                byte[] meterCodeBytes = meterCode.getBytes(StandardCharsets.US_ASCII);
                byte[] paddedMeterCode = new byte[16];
                System.arraycopy(meterCodeBytes, 0, paddedMeterCode, 0, Math.min(meterCodeBytes.length, 16));
                buffer.put(paddedMeterCode);
            }
            
            // 6. 厂商标识：[46-47] 共2字节，厂商名称2位字母转换成HEX
            String vendorCode = sendData.getVendorCode();
            if (vendorCode != null && vendorCode.equalsIgnoreCase("NONE")) {
                // 如果厂家代码是NONE，将字节全置FF
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else if (vendorCode != null && vendorCode.length() >= 2) {
                buffer.put((byte) vendorCode.charAt(0));
                buffer.put((byte) vendorCode.charAt(1));
            }
            
            // 7. 服务端IP地址及端口：[48-57] 共10字节，IP地址4字节，子网掩码4字节，端口2字节
            String ipAddress = sendData.getIpAddress();
            String ipMask = sendData.getIpMask();
            String port = sendData.getPort();
            if (ipAddress != null && ipAddress.equalsIgnoreCase("NONE")) {
                // 如果IP地址是NONE，将字节全置FF
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else if (ipAddress != null && !ipAddress.isEmpty()) {

                // 处理标准IP地址格式，如 "192.168.1.1"
                String[] ipParts = ipAddress.split("\\.");
                for (String part : ipParts) {
                    buffer.put((byte) Integer.parseInt(part));
                }
            }
            // 解析子网掩码
            if (ipMask != null && ipMask.equalsIgnoreCase("NONE")) {
                // 如果子网掩码是NONE，将字节全置FF
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else if (ipMask != null && !ipMask.isEmpty()) {
                // 处理标准子网掩码格式，如 "255.255.255.0"
                String[] maskParts = ipMask.split("\\.");
                for (String part : maskParts) {
                    buffer.put((byte) Integer.parseInt(part));
                }
            } else {
                // 默认子网掩码 255.255.255.0
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            }

            // 解析端口,先判断是不是NONE，再判断其他的
            if (port != null && port.equalsIgnoreCase("NONE")) {
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else if (port != null && !port.isEmpty()) {
                int portValue = Integer.parseInt(port);
                buffer.put((byte) ((portValue >> 8) & 0xFF)); // 高位
                buffer.put((byte) (portValue & 0xFF));        // 低位
            }
            
            // 8. 正累计数据：[58-61] 共4字节
            String positiveData = sendData.getPositiveData();
            if (positiveData != null && positiveData.equalsIgnoreCase("NONE")) {
                // positiveData为NONE时设置为FF
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else {
                writeOptionalIntAs4Bytes(buffer, sendData.getPositiveData());
            }

            // 9. 负累计数据：[62-65] 共4字节
            String negativeData = sendData.getNegativeData();
            if (negativeData != null && negativeData.equalsIgnoreCase("NONE")) {
                // negativeData为NONE时设置为FF
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
                buffer.put((byte) 0xFF);
            } else {
                writeOptionalIntAs4Bytes(buffer, sendData.getNegativeData());
            }

            // 10. 上线间隔：[66-66] 共1字节
            String onlineInterval = sendData.getOnlineInterval();
            if (onlineInterval != null && onlineInterval.equalsIgnoreCase("NONE")) {
                // 如果上线间隔是NONE，将字节置FF
                buffer.put((byte) 0xFF);
            } else if (onlineInterval != null && !onlineInterval.isEmpty()) {
                // 解析十六进制字符串为字节值
                buffer.put((byte) Integer.parseInt(onlineInterval, 16));
            }
            
            // 11. 重发次数：[67-67] 共1字节
            String retryCount = sendData.getRetryCount();
            if (retryCount != null && retryCount.equalsIgnoreCase("NONE")) {
                // 如果重发次数是NONE，将字节置FF
                buffer.put((byte) 0xFF);
            } else if (retryCount != null && !retryCount.isEmpty()) {
                // 解析十六进制字符串为字节值
                buffer.put((byte) Integer.parseInt(retryCount, 16));
            }

            // 12. CRC16：[68-69] 共2字节，CRC16 ModBus校验
            // CRC16从第3位开始至第67位，共65字节（跳过前导符）
            byte[] dataForCrc = new byte[65];
            buffer.position(2); // 跳过前导符
            buffer.get(dataForCrc);
            
            short crc16 = calculateCrc16(dataForCrc);
            buffer.put((byte) (crc16 & 0xFF));        // 低位在前
            buffer.put((byte) ((crc16 >> 8) & 0xFF)); // 高位在后
            
            // 13. 结束符：[70-70] 共1字节，0x16
            buffer.put((byte) 0x16);
            
            return buffer.array();
        } catch (Exception e) {
            logger.error("生成下发数据指令失败: {}", e.getMessage());
            throw new RuntimeException("生成下发数据指令失败", e);
        }
    }

    /**
     * 解析32字节上行确认指令
     * @param data 32字节数据
     * @return
     */
    @Override
    public ConfirmResponse parseConfirmResponse(byte[] data) {
        try {
            if (data == null || data.length != 32) {
                logger.warn("上行确认指令长度不正确，期望32字节，实际{}字节", data != null ? data.length : 0);
                return new ConfirmResponse("", "", false);
            }

            ByteBuffer buffer = ByteBuffer.wrap(data);
            // 43 47
            // 1. 前导符：[1-2] 共2字节，0x43 0x47
            byte prefix1 = buffer.get();
            byte prefix2 = buffer.get();
            if (prefix1 != 0x43 || prefix2 != 0x47) {
                logger.warn("上行确认指令前导符错误，期望0x43 0x47，实际0x{} 0x{}", 
                    String.format("%02X", prefix1 & 0xFF), String.format("%02X", prefix2 & 0xFF));
                return new ConfirmResponse("", "", false);
            }
            // 32 30 31 32 37 30 66 FD 3A 00
            // 2. 指令编号：[3-12] 共10字节
            byte[] instructionBytes = new byte[10];
            buffer.get(instructionBytes);
            String instructionId = new String(instructionBytes, StandardCharsets.US_ASCII).trim();
            // 01
            // 3. 指令类型：[13-13] 共1字节，0x04：表端上行确认指令
            int instructionType = buffer.get() & 0xFF;
            if (instructionType != 0x04) {
                logger.warn("上行确认指令类型错误，期望0x04，实际0x{}", String.format("%02X", instructionType));
                return new ConfirmResponse(instructionId, "", false);
            }
            // 38 36 34 37 37 33 30 36 30 30 32 30 31 32 37 30
            // 4. IMEI：[14-29] 共16字节
            byte[] imeiBytes = new byte[16];
            buffer.get(imeiBytes);
            String imei = new String(imeiBytes, StandardCharsets.US_ASCII).trim();

            // 5. CRC16：[30-31] 共2字节
            byte[] receivedCrc = new byte[2];
            buffer.get(receivedCrc);
            System.out.println("CRC16" +  (receivedCrc[0] & 0xFF) + " " + (receivedCrc[1] & 0xFF));

            byte endFlag = buffer.get();
            if (endFlag != 0x16) {
                logger.warn("上行确认指令结束符错误，期望0x16，实际0x{}", String.format("%02X", endFlag & 0xFF));
                return new ConfirmResponse(instructionId, imei, false);
            }

            // 验证CRC16
            // CRC16从第3位开始至第29位，共27字节（跳过前导符）
            byte[] dataForCrc = new byte[27];
            System.arraycopy(data, 2, dataForCrc, 0, 27);
            boolean crcValid = verifyCrc16(dataForCrc, receivedCrc);
            
            if (crcValid) {
                logger.info("上行确认指令解析成功，指令编号: {}, IMEI: {}", instructionId, imei);
            } else {
                logger.warn("上行确认指令CRC16校验失败，指令编号: {}, IMEI: {}", instructionId, imei);
            }
            
            return new ConfirmResponse(instructionId, imei, crcValid);
        } catch (Exception e) {
            logger.error("解析上行确认指令失败: {}", e.getMessage(), e);
            return new ConfirmResponse("", "", false);
        }
    }

    /**
     * 发送确认指令给TCP客户端
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     * @param command 确认指令
     * @return
     */
    @Override
    public boolean sendConfirmCommand(String clientIp, int clientPort, byte[] command) {
        try {
            boolean success = tcpConnectionService.sendDataToClient(clientIp, clientPort, command);
            
            if (success) {
                // 发送成功后等待3秒再关闭连接，确保客户端有足够时间接收数据
                Thread.sleep(3000);
                tcpConnectionService.closeConnection(clientIp, clientPort);
            }
            
            return success;
        } catch (Exception e) {
            logger.error("发送确认指令失败 {}:{} 错误:{}", clientIp, clientPort, e.getMessage());
            return false;
        }
    }

    /**
     * 发送下发数据指令给TCP客户端
     * @param clientIp 客户端IP
     * @param clientPort 客户端端口
     * @param command 下发数据指令
     * @return
     */
    @Override
    public boolean sendDataCommand(String clientIp, int clientPort, byte[] command) {
        try {
            return tcpConnectionService.sendDataToClient(clientIp, clientPort, command);
        } catch (Exception e) {
            logger.error("发送下发数据指令失败 {}:{} 错误:{}", clientIp, clientPort, e.getMessage());
            return false;
        }
    }

    /**
     * 计算CRC16 ModBus校验码
     */
    private short calculateCrc16(byte[] data) {
        int crc = 0xFFFF;
        for (byte b : data) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) {
                    crc >>= 1;
                    crc ^= 0xA001;
                } else {
                    crc >>= 1;
                }
            }
        }
        return (short) crc;
    }

    /**
     * 将十六进制字符串形式的指令编号转换为原始字节数组。
     * 如果入参为空或长度不是偶数，则返回空字节数组以避免异常。
     */
    private byte[] decodeInstructionId(String hex) {
        if (hex == null) {
            return new byte[0];
        }
        String normalized = hex.trim();
        if ((normalized.length() % 2) != 0) {
            // 长度不为偶数，无法解析，返回空
            return new byte[0];
        }
        int len = normalized.length();
        int outLen = len / 2;
        byte[] out = new byte[outLen];
        for (int i = 0; i < len; i += 2) {
            int hi = Character.digit(normalized.charAt(i), 16);
            int lo = Character.digit(normalized.charAt(i + 1), 16);
            if (hi == -1 || lo == -1) {
                // 非法hex字符，停止并返回已解析部分
                return java.util.Arrays.copyOf(out, i / 2);
            }
            out[i / 2] = (byte) ((hi << 4) + lo);
        }
        return out;
    }

    /**
     * 验证CRC16 ModBus校验码
     */
    private boolean verifyCrc16(byte[] data, byte[] receivedCrc) {
        if (receivedCrc == null || receivedCrc.length != 2) {
            return false;
        }
        short calculatedCrc = calculateCrc16(data);
        short receivedCrcValue = (short) (((receivedCrc[1] & 0xFF) << 8) | (receivedCrc[0] & 0xFF));
        return calculatedCrc == receivedCrcValue;
    }

    @Override
    public boolean updateSendStatusByInstructionId(String instructionId, int status) {
        try {
            int updatedRows = deviceSendDataMapper.updateSendStatusByInstructionId(instructionId, status, java.time.LocalDateTime.now());
            boolean success = updatedRows > 0;
            if (!success) {
                logger.warn("更新发送状态失败 指令编号:{} 更新行数:{}", instructionId, updatedRows);
            }
            return success;
        } catch (Exception e) {
            logger.error("更新发送状态失败 指令编号:{} 状态:{} 错误:{}", instructionId, status, e.getMessage());
            return false;
        }
    }

    /**
     * 字节数组转十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X", b & 0xFF));
        }
        return result.toString();
    }

    /**
     * 将可选的数值字符串写为4字节：
     * - null/空串/全0（"0","00","00000000"等）→ 写入 0xFF 0xFF 0xFF 0xFF
     * - 对于正累计数据和负累计数据：按"前3字节整数 + 后1字节小数"格式处理
     * - 其他情况按原逻辑处理
     */
    private void writeOptionalIntAs4Bytes(java.nio.ByteBuffer buffer, String numberString) {
        if (numberString == null) {
            for (int i = 0; i < 4; i++) buffer.put((byte) 0xFF);
            return;
        }
        String s = numberString.trim();
        if (s.isEmpty()) {
            for (int i = 0; i < 4; i++) buffer.put((byte) 0xFF);
            return;
        }
        // 判断是否"全0"
        boolean allZero = true;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) != '0') { allZero = false; break; }
        }
        if (allZero) {
            for (int i = 0; i < 4; i++) buffer.put((byte) 0xFF);
            return;
        }

        try {
            // 检查是否包含小数点，用于正累计和负累计数据
            if (s.contains(".")) {
                // 处理小数格式：前3字节整数 + 后1字节小数
                String[] parts = s.split("\\.");
                if (parts.length == 2) {
                    int integerPart = Integer.parseInt(parts[0]);
                    int decimalPart = Integer.parseInt(parts[1]);
                    
                    // 确保整数部分不超过3字节范围 (0-16777215)
                    if (integerPart > 0xFFFFFF) {
                        logger.warn("整数部分超出范围: {}, 使用最大值", integerPart);
                        integerPart = 0xFFFFFF;
                    }
                    
                    // 确保小数部分不超过1字节范围 (0-255)
                    if (decimalPart > 0xFF) {
                        logger.warn("小数部分超出范围: {}, 使用最大值", decimalPart);
                        decimalPart = 0xFF;
                    }
                    
                    // 写入前3字节（整数部分）
                    buffer.put((byte) ((integerPart >> 16) & 0xFF));
                    buffer.put((byte) ((integerPart >> 8) & 0xFF));
                    buffer.put((byte) (integerPart & 0xFF));
                    // 写入后1字节（小数部分）
                    buffer.put((byte) decimalPart);
                } else {
                    // 格式错误，使用默认值
                    for (int i = 0; i < 4; i++) buffer.put((byte) 0xFF);
                }
            } else {
                // 处理整数格式
                int value;
                String ns = s;
                
                if (ns.matches("\\d+")) {
                    // 纯数字字符串，按10进制解析
                    long decimalValue = Long.parseLong(ns);
                    // 转换为16进制标识（4字节）
                    value = (int) decimalValue;
                } else if (ns.startsWith("0x") || ns.startsWith("0X")) {
                    // 已经是16进制格式
                    value = (int) Long.parseLong(ns.substring(2), 16);
                } else if (ns.matches("[0-9A-Fa-f]{1,8}")) {
                    // 纯16进制字符串（不带0x）
                    value = (int) Long.parseLong(ns, 16);
                } else {
                    // 其他情况按十进制解析
                    value = Integer.parseInt(ns);
                }
                buffer.putInt(value);
            }
        } catch (Exception ex) {
            // 解析失败则按未下发处理
            logger.warn("解析数值字符串失败: {}, 使用默认值 0xFF", numberString);
            for (int i = 0; i < 4; i++) buffer.put((byte) 0xFF);
        }
    }
}